package p.ithorns.framework.idempotent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * IdempotentInterceptor
 * 使用自定义拦截器拦截重复请求
 * <p>
      需要在WebAppConfigurer中设置
      public void addInterceptors(InterceptorRegistry registry) {
           registry.addInterceptor(new IdempotentInterceptor(redis)).addPathPatterns("/**");
      }
   </p>
 和使用Aop拦截方式比较，Interceptor会拦截所有请求，在request处理上更加方便，
 而Aop方式根据条件拦截指定方法，更加精准和灵活, request处理上需要依赖RequestContextHolder
 * @author ithorns
 * @version 1.0
 * @date 2019/4/29 18:07
 */
public class IdempotentInterceptor implements HandlerInterceptor {

    private final static Logger log = LoggerFactory.getLogger(IdempotentInterceptor.class);

    private final StringRedisTemplate redis;


    public IdempotentInterceptor(StringRedisTemplate redis) {
        this.redis = redis;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod  = (HandlerMethod ) handler;
        Method method = handlerMethod.getMethod();
        // String methodName = method.getName();

        /*
         * 判断方法是否为写请求，两种方法
         * 1. 判断方法名
         * 2. 使用自定义annotation 更灵活
         */

        // 没有注解的方法，略过
        Idempotent idpt = method.getAnnotation(Idempotent.class);
        if (Objects.isNull(idpt)) {
            return true;
        }


        //if(methodName.matches(METHOD_PREFIX) || methodName.matches(METHOD_SUFFIX)) {
            Enumeration enums = request.getParameterNames();

            StringBuilder sBuf = new StringBuilder();
            while (enums.hasMoreElements()) {
                String param = (String)enums.nextElement();
                String paramValue = request.getParameter(param);
                sBuf.append(param).append("=").append(paramValue).append("&");
            }
            MessageDigest md5 = MessageDigest.getInstance("md5");
            String key = new String(md5.digest(sBuf.toString().getBytes()));
            Boolean success = redis.opsForValue().setIfAbsent(key, key);
            if (success) {
                redis.expire(key, 20000, TimeUnit.MILLISECONDS);
                log.info("第1次请求");
                return true;
            } else {
                log.info("第N次请求");
                response.setCharacterEncoding("UTF-8");
                PrintWriter pw = response.getWriter();
                pw.write("请勿重复请求");
                return false;
            }
        //}
        //return true;
    }
}
